import serial
import time
import argparse
import subprocess
import datetime
import os
import threading
import tkinter as tk

uos_wait_reboot_thread_should_stop=False
reboot_times=1
uos_wait_timeout=False

uos_cmd_list_disconnect=[
    "adb -d forward --remove-all",
    "adb -d disconnect",
]

uos_cmd_list_sos_reboot=[
    "adb -d reboot"
]

uos_cmd_list_wait_android=[
    "adb -d forward tcp:7666 tcp:6666",
    "adb connect 127.0.0.1:7666",
    "adb -s 127.0.0.1:7666 root",
    "adb -s 127.0.0.1:7666 shell 'ls /data'"
]
uos_cmd_list_wait_tbox=[
    "adb -d forward tcp:7667 tcp:6667",
    "adb connect 127.0.0.1:7667",
    "adb -s 127.0.0.1:7667 shell 'ls /data'"
]
uos_cmd_list_reboot=[
    "adb -s 127.0.0.1:7667 reboot", #reboot tbox first
    "adb -s 127.0.0.1:7666 reboot", #reboot android next
]


def show_auto_close_popup():
    # 创建隐藏主窗口
    root = tk.Tk()
    root.withdraw()
    # 创建弹窗
    popup = tk.Toplevel(root)
    popup.title("脚本状态")
    popup.geometry("300x150")
    # 1. 绑定关闭协议（关键修复）
    def on_closing():
        if popup.winfo_exists():  # 确保窗口未销毁[7](@ref)
            popup.destroy()
        if root.winfo_exists():
            root.destroy()  # 确保主窗口退出[5](@ref)
    popup.protocol("WM_DELETE_WINDOW", on_closing)  # 绑定关闭事件[6](@ref)
    # 添加消息内容
    msg_label = tk.Label(popup, text="脚本运行成功！", font=("Arial", 12))
    msg_label.pack(pady=20)
    # 2. 自动关闭机制
    def auto_close():
        for remaining in range(4, 0, -1):
            if not popup.winfo_exists():  # 窗口已被手动关闭则终止[7](@ref)
                return
            time.sleep(1)
        on_closing()  # 安全调用关闭函数
    # 3. 使用独立线程避免阻塞（关键修复）
    threading.Thread(target=auto_close, daemon=True).start()
    popup.mainloop()


def run_cmd(cmd, timeout=4):
    is_cmd_ok=False
    result='no msg'
    try:
        output = ''
        result = subprocess.run(cmd,
                                shell=True,
                                capture_output=True,
                                text=True,
                                encoding='utf-8',
                                timeout=timeout if timeout > 0 else None)
        if result.stdout:
            output = result.stdout.strip()
        if result.returncode != 0:
            is_cmd_ok=False
            print('failed:')
            print(f'    {cmd}')
            print(f'    output:{output}')
        else:
            print('done:')
            print(f'    {cmd}')
            is_cmd_ok=True
    except subprocess.TimeoutExpired as e:
        print("timeout:")
        print(f"    {cmd},{e}")
    except Exception as e:
        print("failed:")
        print(f"    {cmd},{e}")
    return is_cmd_ok


def run_cmd_no_check(cmd):
    result='no msg'
    try:
        result = subprocess.run(cmd,
                                shell=True,
                                capture_output=False,
                                text=True,
                                encoding='utf-8',
                                timeout=None)
    except Exception as e:
        print("done:")
        print(f"{cmd},{result}")

string_filter_in='RT throttling'
string_filter_oo='vmid:'


# def serial_log_check(serial_buffer):
#     if string_filter_in in serial_buffer:
#         if string_filter_oo in serial_buffer:
#             return False
#         else:
#             return True #has 'RT throttling' and no 'vmid:'


def serial_log_check(serial_buffer):
    if string_filter_in in serial_buffer:
        return True #has 'RT throttling'
    else:
        return False


def serial_log_record(port='/dev/ttyUSB0',baudrate=921600,timeout=1,dir_path="temp_failed"):
    global uos_wait_timeout
    serial_file=dir_path+"/serial_log.txt"
    ret=False
    with open(serial_file, 'a', encoding='utf-8') as file:
        try:
            serial_log=serial.Serial(port,baudrate,timeout=timeout)
            while True:
                if serial_log.in_waiting >0:
                    try:
                        log_seg=serial_log.readline().decode('utf-8')
                        if log_seg:
                            file.write(log_seg)
                            file.flush()
                            if serial_log_check(log_seg):
                                uos_wait_reboot_thread_should_stop=True
                                ret=True
                                break
                    except UnicodeDecodeError as e:
                        print('error:')
                        print(f"    {e}")
                if True==uos_wait_timeout:
                    print('some uos wait timeout, should kick and reconnect')
                    file.write(" ")
                    file.write(" ")
                    file.write("some uos wait timeout, should kick and reconnect")
                    file.flush()
                    ret=False
                    break
        except KeyboardInterrupt:
            print('        interrupt by ctrl+c')
        except serial.SerialException as e:
            print('error:')
            print(f"    {e}")
        finally:
            if serial_log.is_open:
                serial_log.close() #close log file
            file.close()
        return ret


def uos_cmd_check_single(cmd):
    retry_cnt_j=4
    while retry_cnt_j>0: #if some cmd failed, wait and retry
        ret=run_cmd(cmd)
        if False==ret:
            retry_cnt_j=retry_cnt_j-1
            time.sleep(2)
        else:
            return True
    if retry_cnt_j<=0:
        return False

def uos_cmd_check(cmds_list):
    retry_cnt_i=4
    ret=False
    while retry_cnt_i>0: #try cmd list some times
        for cmd in cmds_list: #foreach cmd list
            ret=uos_cmd_check_single(cmd)
            if False==ret:
                break
        if True==ret: #last cmd return True,nothing to be executing,so return
            return True
        else:
            retry_cnt_i=retry_cnt_i-1
            time.sleep(10) #all cmd failed,wait and retry
            print('retry all cmd')
    return False

def uos_cmd_no_check(cmds_list):
    for cmd in cmds_list:
        run_cmd_no_check(cmd)

def wait_for_time(wait_time):
    while wait_time>0:
        print(f"\rtime left: {wait_time} s", end="")
        time.sleep(1)
        wait_time=wait_time-1
    print()

def uos_wait_reboot():
    global reboot_times,uos_wait_reboot_thread_should_stop,uos_wait_timeout
    reboot_interval=30
    while True:
        print("================"+str(reboot_times)+" times================")
        run_cmd_no_check("echo "+str(reboot_times)+" times >> accumulate_times.txt")
        if True==uos_wait_reboot_thread_should_stop: #stop this call
            print(f'uos_wait_reboot_thread_should_stop={uos_wait_reboot_thread_should_stop}')
            return
        uos_cmd_no_check(uos_cmd_list_disconnect)
        if True==uos_wait_reboot_thread_should_stop: #stop this call
            print(f'uos_wait_reboot_thread_should_stop={uos_wait_reboot_thread_should_stop}')
            return
        ret=uos_cmd_check(uos_cmd_list_wait_android)
        if ret==False:
            print('uos wait failed, android')
            break
        if True==uos_wait_reboot_thread_should_stop: #stop this call
            print(f'uos_wait_reboot_thread_should_stop={uos_wait_reboot_thread_should_stop}')
            return
        ret=uos_cmd_check(uos_cmd_list_wait_tbox)
        if ret==False:
            print('uos wait failed, tbox')
            break
        if True==uos_wait_reboot_thread_should_stop: #stop this call
            print(f'uos_wait_reboot_thread_should_stop={uos_wait_reboot_thread_should_stop}')
            return
        uos_cmd_no_check(uos_cmd_list_reboot)
        print(f'uos reboot done,wait {reboot_interval} s for next')
        wait_for_time(reboot_interval)
        reboot_times=reboot_times+1
    uos_wait_timeout=True #break, remove all adb forward

def uos_reboot_test():
    global reboot_times,uos_wait_reboot_thread_should_stop
    ret=False
    timestamp = datetime.datetime.now().strftime("%Y-%m%d-%H%M")
    timestamp_isexist=os.path.exists(timestamp)
    if not timestamp_isexist:
        os.makedirs(timestamp,0o755)
    # uos wait and reboot thread
    t_uos_wait_reboot=threading.Thread(target=uos_wait_reboot,args=())
    t_uos_wait_reboot.start()
    # push yocto trace sh
    run_cmd("adb -s EAS4ZXONEMCAGQNR_YOCTO shell 'mount -o rw,remount /'")
    run_cmd("adb -s EAS4ZXONEMCAGQNR_YOCTO push ./dev_scripts/yocto_trace.sh /data/")
    run_cmd("adb -s EAS4ZXONEMCAGQNR_YOCTO shell 'chmod 777 /data/yocto_trace.sh'")
    # monitor serial log
    ret=serial_log_record(dir_path=timestamp)
    uos_wait_reboot_thread_should_stop=True
    print("****************"+str(reboot_times)+" times catches****************")
    # get yocto trace
    run_cmd("adb -s EAS4ZXONEMCAGQNR_YOCTO shell '/data/yocto_trace.sh -b'")
    run_cmd("adb -s EAS4ZXONEMCAGQNR_YOCTO shell '/data/yocto_trace.sh -e'")
    run_cmd("adb -s EAS4ZXONEMCAGQNR_YOCTO shell 'cat /sys/kernel/debug/bootticks >> /data/SYS_FTRACE'")
    # get nebula trace
    run_cmd("adb -s EAS4ZXONEMCAGQNR_YOCTO shell 'echo 1 > /sys/kernel/debug/nebula_trace/ktrace/snapshot'")
    # pull trace;
    run_cmd("adb -s EAS4ZXONEMCAGQNR_YOCTO pull /data/ktrace_snapshot.raw "+timestamp+"/ktrace_snapshot.raw")
    run_cmd("adb -s EAS4ZXONEMCAGQNR_YOCTO pull /data/SYS_FTRACE "+timestamp+"/SYS_FTRACE")
    run_cmd_no_check("python3 ./debug-tool/trace/parser/parse.py -f "+timestamp+"/SYS_FTRACE -k "+timestamp+"/ktrace_snapshot.raw  -o "+timestamp+"/full_trace.log")
    if True==ret:
        run_cmd_no_check("echo "+str(timestamp)+" check+++++++++ >> accumulate_times.txt")
    show_auto_close_popup() #popup message
    time.sleep(10)
    print('wait for pthread join')
    uos_wait_reboot_thread_should_stop=True
    t_uos_wait_reboot.join()
    

if __name__ == '__main__':
    reboot_times=1
    max_times=4444
    while max_times>0:
        uos_reboot_test()
        if True==uos_wait_timeout:
            uos_cmd_no_check(uos_cmd_list_sos_reboot) #sos reboot
            time.sleep(120)
        uos_wait_timeout=False #
        uos_wait_reboot_thread_should_stop=False #uos_wait_reboot thread already stopped
        max_times=max_times-1
